#pragma once

#include <unistd.h>
#include <sys/epoll.h>

#include <cstring>

#include "nocopy.hpp"
#include "log.hpp"
extern Log log;

class Epoller : public nocopy
{
public:
    static const size_t Size = 128;
public:
    enum Epoll_Error
    {
        EPOLL_CREATE_ERROR = 1,
        EPOLL_WAIT_ERROR = 2,
        EPOLL_CONTROL_ERROR = 3
    };
    enum Epoll_FD_Control
    {
        FD_ADD = EPOLL_CTL_ADD,
        FD_DEL = EPOLL_CTL_DEL,
        FD_MOD = EPOLL_CTL_MOD
    };
    enum Epoll_Event
    {
        EVENT_IN = EPOLLIN,
        EVENT_OUT = EPOLLOUT,
        EVENT_PRI = EPOLLPRI,
        EVENT_ERR = EPOLLERR,
        EVENT_HUP = EPOLLHUP,
        EVENT_ET = EPOLLET,
        EVENT_ONCE = EPOLLONESHOT
    };
public:
    Epoller(int size = Size, int timeout = 1000)
        : _size(size)
        , _timeout(timeout)
    {
        _epoll_fd = epoll_create(_size);
        if (_epoll_fd < 0)
        {
            log(Fatal, "epoll create error, errno: %d, err string: ", errno, strerror(errno));
            exit(EPOLL_CREATE_ERROR);
        }
        log(Info, "epoll fd create success, fd: %d", _epoll_fd);
    }

    int control(int fd, int oper, uint32_t event = 0)
    {
        int n = 0;
        if (oper == FD_DEL)
            n = epoll_ctl(_epoll_fd, oper, fd, nullptr);
        else
        {
            struct epoll_event ep_event;
            ep_event.events = event;
            ep_event.data.fd = fd;
            n = epoll_ctl(_epoll_fd, oper, fd, &ep_event);
        }
        if (n < 0)
        {
            log(Fatal, "epoll control error, errno: %d, err string: %s", errno, strerror(errno));
            exit(EPOLL_CONTROL_ERROR);
        }
        return n;
    }

    int wait(struct epoll_event* revent, int num)
    {
        int n = epoll_wait(_epoll_fd, revent, num, _timeout);
        if (n < 0)
        {
            log(Fatal, "epoll wait error, errno: %d, err string: ", errno, strerror(errno));
            exit(EPOLL_WAIT_ERROR);
        }
        return n;
    }

    ~Epoller()
    {
        if (_epoll_fd > 0)
            close(_epoll_fd);
    }
private:
    int _epoll_fd = -1;
    int _size = Size;
    int _timeout = 1000;
};